#include <iostream>
#include "serversocket.h"
#include "initializer_list.h"
#include "range_based_for.h"
#include "structured_binding.h"
#include "stl_new_functions.h"
#include "smart_ptr.h"

using namespace std;

#pragma comment(lib, "ws2_32.lib")

//[[noreturn]] C++11
//[[fallthrough]]; C++17
//[[nodiscard]] C++17
//[[maybe_unused]] C++17
//[[deprecated("use to_string instead")]] C++14

#define TEST_ServerSocket 0
#define TEST_INITIALIZER_LIST 0
#define TEST_RangeBasedFor 0
#define TEST_StructuredBinding 0
#define TEST_StlNewFunctions 0
#define TEST_SmartPointer 1

void printVersion();
int main(int argc, char* argv[])
{
#if TEST_SmartPointer

    CSmartPtr::testAutoPtr();
    CSmartPtr::testAutoPtr2();
    CSmartPtr::testUniquePtr3();
    CSmartPtr::testUniquePtr4();
    CSmartPtr::testUniquePtr5();
    CSmartPtr::testSharedPtr6();
    CSmartPtr::testSharedPtr7();
    CSmartPtr::testSharedPtr8();
    CSmartPtr::testWeakPtr8();
    CSmartPtr::testWeakPtr9();
    CSmartPtr::testSmartPtr10();
    CSmartPtr::testSmartPtr11();

#endif

#if TEST_StlNewFunctions
    CStlNewFunctions::emplaceBack();
    CStlNewFunctions::tryEmplace();
    CStlNewFunctions::tryEmplace2();
    CStlNewFunctions::tryEmplace3();
    CStlNewFunctions::tryEmplace4();
    CStlNewFunctions::tryEmplace5();
    CStlNewFunctions::insertOrAssign();
#endif

#if TEST_StructuredBinding
    CStructuredBinding::mapInsert();
    CStructuredBinding::tupleLearn();
#endif

#if TEST_RangeBasedFor

    CForA forA1(1);
    CForA forA2(2);
    CForA forA3(3);
    std::vector<CForA> forV;
    forV.push_back(forA1);
    forV.push_back(forA2);
    forV.push_back(forA3);
    std::cout << "begin range-based-for" << std::endl;
    for (auto& iter : forV) {
        iter.m = iter.m + 2;
    }

    for (const auto& iter : forV) {
        std::cout << "iter:" << iter.m << " ";
    }
    std::cout << "\nend range-based-for" << std::endl;

    CForB<int, 10> forB;
    for (auto iter : forB) {
        std::cout << iter << " ";
    }
    std::cout << std::endl;

#endif

#if TEST_INITIALIZER_LIST
    A a {1, 2, 4, 3};
    a.print();
    a.append({9, 8, 6, 7});
    std::cout << "after appending..." << std::endl;
    a.print();

    Json array_not_object = Json::array({ {"currency", "USD"}, {"value", 42.99} });
    array_not_object.toString();

#endif

#if TEST_ServerSocket
    CServerSocket serverSocket;
    if (!serverSocket.DoInit()) {
        printf("error init...\n");
        return -1;
    }

    if (!serverSocket.DoBind("0.0.0.0")) {
        printf("error bind...\n");
        return -2;
    }

    if (!serverSocket.DoListen()) {
        printf("error listen...\n");
        return -3;
    }

    if (!serverSocket.DoAccept()) {
        printf("error accept...\n");
        return -4;
    }
#endif

    printVersion();
    system("pause");

    return 0;
}

void printVersion()
{
    std::cout << "Hello World!" << std::endl;
    std::cout << "c++ version = " << __cplusplus << std::endl;

#ifdef _MSC_VER
    std::cout << "MSVC version = " << _MSC_VER << std::endl;
#endif // _MSC_VER

#ifdef __GNUC__
    std::cout << "gcc version = " << __VERSION__ << std::endl;
    std::cout << "gcc major = " << __GNUC__ << std::endl;
    std::cout << "gcc minor = " << __GNUC_MINOR__ << std::endl;
    std::cout << "gcc patch = " << __GNUC_PATCHLEVEL__ << std::endl;
#endif // __GNUC__
}